package oboe.utils;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.utils.ByteArray;

import oboe.core.Oboe;
import oboe.math.Mesh3D;
import oboe.math.Number2D;
import oboe.math.Number3D;
import oboe.math.Vertex3D;

import flash.Vector;

class MD2Parser extends EventDispatcher
{
public var data:Mesh3D;
private var rawData:ByteArray;
private var state:Function;
private var head:Header;

public function new( data:ByteArray )
{
	super();
	this.rawData = data;
	Oboe.stage.addEventListener(Event.ENTER_FRAME, onStep);
	this.state = parseHeader;
}

private var stepId:Int=0;
private function onStep( e:Event ):Void
{
	for( var i:Int=0; i<20; ++i )
	{
	this.state();
	}
	// trace("step id:", ++stepId);
}

private function parseHeader():Void
{
	this.head = new Header( rawData );
	this.state = readBody;
}

private function readBody():Void
{
	readUVs();
	readIndices();
	readFrames();
	this.state = setup1;
}

private var uvlist:Vector<Float>;
private var frames:Vector< Vector.<Number3D> >;
private var lf:Int;
private var frame:Vector<Number3D>;
private var vframe:Vector<Vertex3D>;

private function setup1():Void
{
	this.uvlist = new Vector<Float>( head.frames[0].length*2 );
	this.frames = new Vector<Vector.<Number3D>>( head.frames.length, true );
	this.lf = frames.length;
	this.vframe = head.frames[0];
	for( var f:Int=0; f<lf; ++f )
	{
	frames[f] = new Vector<Number3D>( head.frames[f].length );
	}
	
	frame = frames[0];
	s2it = 0;
	s2l = head.indices.length;
	s2fl =frame.length-1;
	this.state = setup2;
}

private var s2it:Int;
private var s2l:Int;
private var s2fl:Int;

private function setup2():Void
{
	var f:Int;
	if( s2it < s2l )
	{
	var uv:Float2D = head.uv[ s2it ];
	var vid:Int = head.indices[s2it];
	var vert:Vertex3D = vframe[ vid ];
	if( !vert.uvl )
	{
		vert.uvl = new Hash();
		vert.uvl[ uv ] = s2it;
		uvlist[ vid*2 ] = uv.x;
		uvlist[ vid*2+1 ] = uv.y;
		for( f=0; f<lf; ++f )
		{
		frames[f][ vid ] = head.frames[f][ vid ];
		}
	}
	else if( uv in vert.uvl )
	{
		vid = head.indices[ vert.uvl[uv] ];
		for( f=0; f<lf; ++f )
		{
		frames[f][ vid ] = head.frames[f][ vid ];
		}
	}
	else
	{
		vert.uvl[ uv ] = s2it;
		head.indices[s2it] = s2fl;
		uvlist[ s2fl*2 ] = uv.x;
		uvlist[ s2fl*2+1 ] = uv.y;
		for( f=0; f<lf; ++f )
		{
		var fvert:Vertex3D = head.frames[f][ vid ];
		frames[f][s2fl] = new Number3D( fvert.x, fvert.y, fvert.z );
		}
		s2fl++;
	}
	
	++s2it;
	}
	else
	{
	this.state = done;
	this.data = new Mesh3D( head.indices, frames, uvlist );
	this.dispatchEvent( new Event( Event.COMPLETE ) );
	}
}

private function done():Void
{
	Oboe.stage.removeEventListener(Event.ENTER_FRAME, onStep);
}
	
private function readUVs() : Void {
	// s.seekg( head.offset_st, ios::beg );
	rawData.position = head.offset_st;
	for( var uvid:Int=0; uvid<head.num_st; ++uvid ){
	//Twin< short > uv(0,0);
	head.uvlist[ uvid*2 ] = rawData.readShort() / head.skinwidth;
	//s.read( (char *)&uv.x, sizeof(short) );
	head.uvlist[ uvid*2+1] = rawData.readShort() / head.skinheight;
	//s.read( (char *)&uv.y, sizeof(short) );
	//r->uvs.push_back( Twin<float>( float( uv.x ) / float(head.skinwidth), float(uv.y) / float(head.skinheight) ) );
	}
}

private function readIndices() : Void {
	// s.seekg( head.offset_tris, ios::beg );
	rawData.position = head.offset_tris;
	for( var tid:Int=0; tid<head.num_tris; ++tid ){
	// Triplet< unsigned short > t;
	head.indices[ tid*3+0 ] = (rawData.readShort() & 0xFFFF);
	head.indices[ tid*3+1 ] = (rawData.readShort() & 0xFFFF);
	head.indices[ tid*3+2 ] = (rawData.readShort() & 0xFFFF);
	// s.read( (char *)&t, sizeof(t) );
	// r->indices.push_back( t );
	
	// read UV indices
	var uvi:Int = (rawData.readShort() & 0xFFFF) * 2;
	head.uv[ tid*3+0 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
	
	uvi = (rawData.readShort() & 0xFFFF) * 2;
	head.uv[ tid*3+1 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
	
	uvi = (rawData.readShort() & 0xFFFF) * 2;
	head.uv[ tid*3+2 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
	/*
	head.indices[ tid*6+3 ] = (rawData.readShort() & 0xFFFF) * 2;
	head.indices[ tid*6+4 ] = (rawData.readShort() & 0xFFFF) * 2;
	head.indices[ tid*6+5 ] = (rawData.readShort() & 0xFFFF) * 2;
	*/
	}
}

private function readFrames() : Void {
	rawData.position = head.offset_frames;
	// s.seekg( head.offset_frames, ios::beg );
	var ws : Float = Oboe.flags["SCALE_3D"];
	for( var fid:Int=0; fid<head.num_frames; ++fid ){
	var scale : Float3D = new Number3D(0,0,0), 
		trans : Float3D = new Number3D(0,0,0);
	var name : String;
	var f:Vector<Vertex3D> = head.frames[fid];
	//r->frames.push_back( f );
	
	scale.readFloat( rawData ).mul(ws);
	// s.read( (char *) &scale, sizeof(scale) );
	trans.readFloat( rawData ).mul(ws);
	// s.read( (char *) &trans, sizeof(trans) );
	rawData.position += 16; // skip name
	// s.read( name, 16 );
	// name[15] = 0;
	// cout << "reading frame " << name << " @" << s.tellg() << endl;
	for( var i:Int=0; i<head.num_vertices; ++i ){
		var v : Vertex3D = new Vertex3D(0,0,0);
		// Triplet<unsigned char> d;
		// Vertex3D v;
		v.readByte( rawData );
		// s.read( (char *)&d, sizeof(d) );
		
		// v = d;
		v.mul3( scale ).add3( trans ).flipYZ();
		// v *= scale;
		// v += trans;
		// swap( v.z, v.y );
		
		f[ i ] = v;
		// f->push_back( v );
		
		rawData.readByte();
		// s.read( (char *)&d, 1 ); // read normal index
	}
	}
}
}
}
import flash.utils.ByteArray;
import flash.utils.Endian;

import oboe.math.Number2D;
import oboe.math.Vertex3D;

internal class Header {
public var  ident : Int;		  /* magic number: "IDP2" */
public var  version : Int;		/* version: must be 8 */

public var  skinwidth : Int;		  /* texture width */
public var  skinheight : Int;		 /* texture height */

public var  framesize : Int;		  /* size in bytes of a frame */

public var  num_skins : Int;		  /* number of skins */
public var  num_vertices : Int;	   /* number of vertices per frame */
public var  num_st : Int;		 /* number of texture coordinates */
public var  num_tris : Int;		   /* number of triangles */
public var  num_glcmds : Int;		 /* number of opengl commands */
public var  num_frames : Int;		 /* number of frames */

public var  offset_skins : Int;	   /* offset skin rawData */
public var  offset_st : Int;		  /* offset texture coordinate rawData */
public var  offset_tris : Int;		/* offset triangle rawData */
public var  offset_frames : Int;	  /* offset frame rawData */
public var  offset_glcmds : Int;	  /* offset OpenGL command rawData */
public var  offset_end : Int;		 /* offset end of file */

public var indices:Vector<Int>;
public var uvlist : Vector< Number >;
public var uv : Vector<Number2D>;
public var frames : Vector< Vector.<Vertex3D> >;

public function Header( rawData : ByteArray ) {
rawData.position = 0;
rawData.endian = Endian.LITTLE_ENDIAN;

ident = rawData.readInt();			 /* magic number: "IDP2" */
version = rawData.readInt();		 /* version: must be 8 */
skinwidth = rawData.readInt();
skinheight = rawData.readInt();
framesize = rawData.readInt();		  /* size in bytes of a frame */

num_skins = rawData.readInt();		  /* number of skins */
num_vertices = rawData.readInt();	   /* number of vertices per frame */
num_st = rawData.readInt();		 /* number of texture coordinates */
num_tris = rawData.readInt();		   /* number of triangles */
num_glcmds = rawData.readInt();		 /* number of opengl commands */
num_frames = rawData.readInt();		 /* number of frames */

offset_skins = rawData.readInt();	   /* offset skin rawData */
offset_st = rawData.readInt();		  /* offset texture coordinate rawData */
offset_tris = rawData.readInt();		/* offset triangle rawData */
offset_frames = rawData.readInt();	  /* offset frame rawData */
offset_glcmds = rawData.readInt();	  /* offset OpenGL command rawData */
offset_end = rawData.readInt();		 /* offset end of file */

indices = new Vector<Int>( num_tris*3, true );
uv = new Vector<Number2D>( num_tris*3, true );
frames = new Vector< Vector.<Vertex3D> >( num_frames, true );
uvlist = new Vector<Float>( num_st*2, true );
for( var i:Int=0; i<num_frames; ++i )
{
	frames[i] = new Vector<Vertex3D>( num_vertices, true );
}
}